chore(contexts): Remove default-trace-ID option#5979
Conversation
04f1690 to
a55366d
Compare
Dav1dde
left a comment
There was a problem hiding this comment.
Thanks, for coming back and cleaning this up.
You accidentally updated/reverted the submodules, sentry-conventions and sentry-native. You might be missing a git submodule update --recursive --init.
274748d to
4654bdc
Compare
4654bdc to
de252d4
Compare
| .0 | ||
| .is_none() | ||
| { | ||
| contexts.add(TraceContext::random(event_id)) |
There was a problem hiding this comment.
If there's a TraceContext with valid information but it's missing trace_id (not sure if this actually occurs in practice) - I think we'll end up overriding the entire context with a new one that has only trace_id? I think we want to get_or_default the trace context, then check whether trace_context.trace_id is null, and then set only trace_context.trace_id if so
| // If the event lacks a TraceContext, add a random one. | ||
|
|
||
| if config.derive_trace_id && !contexts.contains::<TraceContext>() { | ||
| if !contexts.contains::<TraceContext>() |
There was a problem hiding this comment.
Can we add unit tests for this logic somewhere?
There was a problem hiding this comment.
Updating the tests is what I'm working on now! :-)
There was a problem hiding this comment.
Just a drive by comment before bed, an integration test (if it doesn't already exist) would be great, we do have a decent framework for them and they work great for regression tests.
de252d4 to
9942b0b
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9942b0b. Configure here.
9942b0b to
e4b0067
Compare
| let trace_ctx = contexts.get_or_default::<TraceContext>(); | ||
| if trace_ctx.trace_id.0.is_none() { | ||
| trace_ctx.trace_id = Annotated::new(TraceId::from(event_id)) | ||
| } | ||
| } |
There was a problem hiding this comment.
Bug: If an event has an existing but empty TraceContext, the normalization logic only sets the trace_id, leaving the required span_id unset, creating an inconsistent state.
Severity: MEDIUM
Suggested Fix
In the else branch where contexts.contains::<TraceContext>() is true, ensure that span_id is also set along with trace_id. This could be done by generating a new span_id if trace_ctx.span_id is None, similar to how trace_id is handled. Alternatively, consider replacing the partial context with a fully new one using TraceContext::random() if the existing one is incomplete.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: relay-event-normalization/src/event.rs#L1370-L1374
Potential issue: When an event is processed that already contains a `TraceContext` but
lacks a `trace_id` (e.g., `{"contexts": {"trace": {}}}`), the normalization logic enters
a branch that sets the `trace_id`. However, this logic fails to also generate and set
the `span_id`. This is inconsistent with the case where no `TraceContext` exists, in
which a new context is created with both a `trace_id` and a `span_id` via
`TraceContext::random()`. The result is a partially normalized trace context where the
`span_id`, a required field, remains `None`.
e4b0067 to
f98e9c9
Compare
|
Adding the skip-changelog label for the Python changelog since it's just updating a couple tests |
f98e9c9 to
c024935
Compare
| assert set(event["contexts"].keys()) == {"browser", "client_os", "trace"} | ||
| else: | ||
| assert "contexts" not in event | ||
| assert set(event["contexts"].keys()) == {"trace"} |
There was a problem hiding this comment.
Should we assert the fields contained in the trace context, e.g. trace_id or other necessary fields are present?
There was a problem hiding this comment.
Generally a good idea, but these are the Python library tests and this is a test for normalizing the user agent, so I think this is fine.
| // If the event lacks a TraceContext, add a random one. | ||
|
|
||
| if config.derive_trace_id && !contexts.contains::<TraceContext>() { | ||
| if !contexts.contains::<TraceContext>() { |
There was a problem hiding this comment.
I think you could collapse this if/else with something like the below (does span_id default have to be added as well?)
let trace_ctx = contexts.get_or_default::<TraceContext>();
if trace_ctx.trace_id.0.is_none() {
trace_ctx.trace_id = Annotated::new(TraceId::from(event_id))
}
There was a problem hiding this comment.
So, there are four possible cases:
- The
TraceContextis not set. In this case, we generate a random SpanID and a TraceID from the EventID. (SeeTraceContext::random.) - The
TraceContextis set with aTraceIDandSpanID. In this case, we want to do nothing. - The
TraceContextis set with noTraceIDbut an existingSpanID. In this case, we want to create newTraceIDbut not change the existingSpanID. - The
TraceContextis set with noTraceIDand noSpanID. In this case, we are currently only generating theTraceID. My reasoning is that "empty context" is different from "missing context" and we should only make the minimal update needed for our purposes (ingesting to EAP), but I could be talked around to updating both for the purpose of consistency with case (1).
All this to say — what does default mean for TraceContext? Is it something with all the attributes set to None? Ideally we'd want to short-circuit to TraceContext::random if the context doesn't exist, not TraceContext::default or whatnot (this method doesn't exist).
There was a problem hiding this comment.
I would say we should defer to @Dav1dde on the question of default span ID (whether or not that should be added, and where that should be added, or if it is already).
But I think the code I attached above works for all of the cases in which trace ID needs to be populated (and additional if branches can be added if needed if there are other defaults we need to set)
There was a problem hiding this comment.
The more I look into this I begin to question whether this doesn't have a lot of unintentional side-effects.
We're generating a trace context here for the purpose of storing in EAP, but that context is also visible to users with side effects as running it through dynamic sampling to mark the sampling decision on the error.
That's more a fundamental concern which also was brought up with the introduction of the feature flag.
I would say we should defer to @Dav1dde on the question of default span ID (whether or not that should be added, and where that should be added, or if it is already).
The span_id is required on the context, that means if you want to generate a context it also needs to be filled with a span_id.
All this to say — what does default mean for TraceContext? Is it something with all the attributes set to None?
All attributes set to Annotated::empty() (practically None). There is an argument to be made, that it should not implement Default at all.
Dav1dde
left a comment
There was a problem hiding this comment.
Looking through it, I noticed some bigger problems. This is actually right now applied to transactions, which makes an existing validation of a trace context being required order dependent, in the worst case it will invent trace or span ids for transactions.
Some of the metadata stuff is inconsistent, with no context metadata is added, with a partial context it is not (presumably because there is this assumption that the schema processor would add it?).
I think we should just not bother with adding metadata to the individual fields when the context is created. If a metadata is desired, we should instead attach it to the context element which was created.
I really don't like that we have to touch a lot of unrelated tests, so I think we should keep the normalization option and that is also a great place to add some documentation why we're doing this.
Relay's normalization is complex enough that I made changes and notes during the review, ended up polishing it and making it a PR with the changes I describe here: #6009
| assert set(event["contexts"].keys()) == {"browser", "client_os", "trace"} | ||
| else: | ||
| assert "contexts" not in event | ||
| assert set(event["contexts"].keys()) == {"trace"} |
There was a problem hiding this comment.
Generally a good idea, but these are the Python library tests and this is a test for normalizing the user agent, so I think this is fine.
There was a problem hiding this comment.
This is another presumably unintentional submodule revert
| // If the event lacks a TraceContext, add a random one. | ||
|
|
||
| if config.derive_trace_id && !contexts.contains::<TraceContext>() { | ||
| if !contexts.contains::<TraceContext>() { |
There was a problem hiding this comment.
The more I look into this I begin to question whether this doesn't have a lot of unintentional side-effects.
We're generating a trace context here for the purpose of storing in EAP, but that context is also visible to users with side effects as running it through dynamic sampling to mark the sampling decision on the error.
That's more a fundamental concern which also was brought up with the introduction of the feature flag.
I would say we should defer to @Dav1dde on the question of default span ID (whether or not that should be added, and where that should be added, or if it is already).
The span_id is required on the context, that means if you want to generate a context it also needs to be filled with a span_id.
All this to say — what does default mean for TraceContext? Is it something with all the attributes set to None?
All attributes set to Annotated::empty() (practically None). There is an argument to be made, that it should not implement Default at all.
| } else { | ||
| let trace_ctx = contexts.get_or_default::<TraceContext>(); | ||
| if trace_ctx.trace_id.0.is_none() { | ||
| trace_ctx.trace_id = Annotated::new(TraceId::from(event_id)) |
There was a problem hiding this comment.
Isn't this inconsistent with TraceContext::random which does add metadata for a "missing" trace id?
| /// Should add a random trace ID to events that lack one. | ||
| pub derive_trace_id: bool, |
There was a problem hiding this comment.
I think we should keep this here, add some documentation why this is needed and then just permanently set it to true where the config gets created.
Usually I'd agree with just by default enabling it, but there is a large impact on tests which are testing unrelated things but now need to assert a random trace id and context.
We can still test the context creation with the boolean and have integration tests to ensure this actually is enabled.
|
|
||
| if config.derive_trace_id && !contexts.contains::<TraceContext>() { | ||
| if !contexts.contains::<TraceContext>() { | ||
| contexts.add(TraceContext::random(event_id)) |
There was a problem hiding this comment.
TraceContext::random interior should not add metadata to the individual fields that they were missing (trace_id/span_id).
If you want to make the trace context effectively required, instead we should add this marker to the context itself.

Shipped this a week ago and have seen no problems. 👍